
#include "OCIDB.h"
#include "db/oracle/odpi/include/dpi.h"
#include <sstream>


static const std::string STRNAME = "oracle";
static const std::string STRVERSION = DPI_VERSION_STRING;


const std::string& OCIDB::name()
{
    return STRNAME;
}

const std::string& OCIDB::version()
{
    return STRVERSION;
}

int OCIDB::version_id()
{
    return DPI_VERSION_NUMBER;
}


OCIDB::OCIDB() : ctx_(nullptr), conn_(nullptr), auto_commit_(true)
{
    err_info_ = new dpiErrorInfo;
}

OCIDB::~OCIDB()
{
    close();
    delete err_info_;
}

void OCIDB::close()
{
    if (ctx_) {
        dpiContext_destroy(ctx_);
        ctx_ = nullptr;
    }
    if (conn_) {
        dpiConn_release(conn_);
        conn_ = nullptr;
    } 
}

bool OCIDB::commit()
{
    return dpiConn_commit(conn_) >= 0;
}

bool OCIDB::roll_back()
{
    return dpiConn_rollback(conn_) >= 0;
}

std::string OCIDB::last_error()
{
    dpiContext_getError(ctx_, err_info_);
    std::stringstream str;
    str << "ERROR: "<< err_info_->message
        << "(" << err_info_->fnName << err_info_->action << ")";
    return str.str();
}


bool OCIDB::_open()
{
    close();

    if (dpiContext_create(DPI_MAJOR_VERSION, DPI_MINOR_VERSION, &ctx_, err_info_) < 0) {
        fprintf(stderr, "ERROR: %.*s (%s : %s)\n", err_info_->messageLength,
            err_info_->message, err_info_->fnName, err_info_->action);
        return false;
    }
    auto con_addr = config.Host + ":" + std::to_string(config.Port) + "/" + config.Database;
    if (dpiConn_create(ctx_,
        config.User.c_str(), config.User.size(), 
        config.Password.c_str(), config.Password.size(),
        con_addr.c_str(), con_addr.size(),
        NULL, NULL, &conn_) < 0) {
        printf("%s\n", last_error().c_str());
        return false;
    }
    
    return true;
}

bool OCIDB::_replace(std::string& sql, size_t& pos, size_t num)
{
    auto np = sql.find("?", pos);
    if (np == std::string::npos) {
        return false;
    }
    auto s = ":" + std::to_string(num);
    sql.replace(np, 1, s);
    pos = np + s.size();
    return true;
}
